package ar.com.e2solver.solver.operator.impl;

import java.util.HashSet;
import java.util.Set;

import ar.com.e2solver.exception.BusinessException;
import ar.com.e2solver.model.Coordinate;
import ar.com.e2solver.model.Piece;
import ar.com.e2solver.model.Puzzle;
import ar.com.e2solver.solver.Individual;
import ar.com.e2solver.solver.RandomConstants;
import ar.com.e2solver.solver.operator.CrossoverOperator;

/**
 * Dado dos individuos crea un tercero por medio del intercambio 
 * random de fichas de las padres
 * @author rodo
 *
 */
public final class SimpleCrossOverOperator implements CrossoverOperator {

	private static SimpleCrossOverOperator instance = new SimpleCrossOverOperator();
	
	private SimpleCrossOverOperator(){}
	
	public static SimpleCrossOverOperator getInstance(){
		return instance;
	}
	
	public Individual operate(Individual individual1, Individual individual2) throws BusinessException {
		// Crea una copia
		Puzzle newPuzzle = new Puzzle(individual1.getPuzzle());
		
		int pieces = newPuzzle.getColumnNumbers() * newPuzzle.getRowsNumber();
		
		Set<Coordinate> coordinates = new HashSet<Coordinate>();
		for (int i = 0; i < pieces/4; i++) {
			coordinates.add(RandomConstants.getRandomCoordinate(newPuzzle.getRowsNumber(), newPuzzle.getColumnNumbers()));
		}
		
		for (Coordinate coordinate : coordinates) {
			doCrossOver(newPuzzle, coordinate, individual2.getPuzzle().getPieceAt(coordinate));
		}
		
		return new Individual(newPuzzle);
	}
	
	private void doCrossOver(Puzzle newPuzzle, Coordinate c1, Piece piece) {
		Coordinate c2 = newPuzzle.findCoordinateForPiece(piece);
		
		// Intercambia las fichas. El intercambio es siempre con las fichas del mismo puzzle
		newPuzzle.swapPositions(c1, c2);
		
		// Reacomoda las fichas si es que eran bordes o esquinas para que coincidan los bordes de su nueva posicion
		if(piece.isABorderPiece()){
			rotateForBorder(c1, newPuzzle);
			rotateForBorder(c2, newPuzzle);
		
		} else if(piece.isACornerPiece()){
			rotateForCorner(c1, newPuzzle);
			rotateForCorner(c2, newPuzzle);
		
		} else {
			rotateEquals(c1, newPuzzle, piece);
		}		
	}

	private void rotateForBorder(Coordinate c, Puzzle p) {
		Piece piece = p.getPieceAt(c);
		int i = c.getX();
		int j = c.getY();
		
		if(i == 0 ){
			while(!piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else if(i > j){
			while(!piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}	
		}
	}

	private void rotateForCorner(Coordinate c, Puzzle p) {
		Piece piece = p.getPieceAt(c);
		int i = c.getX();
		int j = c.getY();
		
		if(i == 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i == 0 && j != 0){
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i != 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		}
	}
	
	private void rotateEquals(Coordinate c1, Puzzle newPuzzle, Piece piece) {
		Piece p = newPuzzle.getPieceAt(c1);
		while(!p.getTop().equals(piece.getTop()) || !p.getRight().equals(piece.getRight())){
			p.rotateToRight(1);
		}
	}
}
